package pers.liaohaolong.mapzoner;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

/*
判定边界时有两种情况
一：
a 0 b    0 1 0
0 0 b => 1 1 0
b b b    0 0 0

b b b b    0 0 0 0
b 0 0 0 => 0 1 1 1
b 0 a a    0 1 0 0
b 0 a a    0 1 0 0
即，两个区域的扩张位置将重合，重合位置为边界位置
二：
a a b    0 1 1
a a b => 1 1 1
b b b    1 1 1

b b b b    0 0 0 0
b b b b => 0 1 1 1
b b a a    0 1 0 0
b b a a    0 1 0 0
即，两个区域的扩张位置将错开，但两个区域的扩张位置都将和另一个区域本体重合，重合位置为边界位置（暂定）
*/
@Slf4j
public class MapZoner {

    /**
     * 背景色，默认为-1
     */
    @Getter
    @Setter
    private int backgroundColorRgb = -1;

    /**
     * 颜色列表
     */
    @Getter
    @Setter(AccessLevel.PRIVATE)
    private Set<Integer> colors = new LinkedHashSet<>();

    /**
     * 边界生成结果
     */
    @Getter
    @Setter(AccessLevel.PRIVATE)
    private boolean[][] boundaries;

    /**
     * 区域扩张结果
     */
    @Getter
    @Setter(AccessLevel.PRIVATE)
    private int[][] zones;

    public boolean colorCheck(@NotNull BufferedImage image) {
        log.info("正在检查...");
        int col = image.getWidth();
        int row = image.getHeight();
        colors.clear();

        // 收集颜色
        for (int y = 0; y < row; y++) {
            for (int x = 0; x < col; x++) {
                colors.add(image.getRGB(x, y));
            }
        }
        // 去除背景色
        if (backgroundColorRgb != -1 && !colors.contains(backgroundColorRgb)) {
            log.info("背景色 {} 未找到，边界生成失败", backgroundColorRgb);
            return false;
        }
        // 颜色数量检查
        if ((backgroundColorRgb == -1 && colors.isEmpty()) || colors.size() == 1) {
            log.info("有效颜色不足，边界生成失败");
            return false;
        }
        return true;
    }

    public void generateBoundaries(@NotNull BufferedImage image) {
        log.info("正在准备...");
        int col = image.getWidth();
        int row = image.getHeight();
        boundaries = new boolean[row][col];
        zones = new int[row][col];

        // 复制图片
        for (int y = 0; y < row; y++) {
            for (int x = 0; x < col; x++) {
                zones[y][x] = image.getRGB(x, y);
            }
        }

        ///////////////////////////////////////////////////////////

        log.info("正在生成边界...");
        /*
        设置一个全域int[][] zones，并直接填入颜色
        进行Math.max(width,height)轮：
            设置int[][] expand，初始化为全-1，标识本轮扩张
            遍历像素：（当前像素位置为A）
                如果不是背景色，则遍历周围8个色块：（当前像素位置为B）
                    如果B是背景色，且expand中B位为-1，在expand中B位设本颜色（允许扩张）
                    如果B是背景色，但expand中为其他色，将boundaries中B位设true（情况一）
                    如果B是其他色，且在右、下、右下位，将boundaries中A位设true（情况二）
         */
        // 辅助变量，指示对应位置是否已扩张过
        boolean[][] mask = new boolean[row][col];
        // 辅助变量，指示本次扩张的像素
        int[][] expand = new int[row][col];
        // 辅助变量，指示最大轮数
        int maxI = Math.max(row, col);
        log.info("正在进行首轮扩张，首轮扩张较慢，请稍后...");
        for (int i = 0; i < maxI; i++) {
            // 初始化
            for (int j = 0; j < row; j++) {
                Arrays.fill(expand[j], -1);
            }
            // 辅助变量，指示本次扩张的像素数
            int expandPixel = 0;
            // 遍历像素
            for (int y = 0; y < row; y++) {
                for (int x = 0; x < col; x++) {
                    if (mask[y][x] || zones[y][x] == backgroundColorRgb) // 已扩张不再扩张，背景色不参与扩张
                        continue;
                    // zones[y][x] a位
                    mask[y][x] = true; // 标记a位已扩张
                    // 8邻域遍历
                    for (int dy = Math.max(0, y - 1); dy <= y + 1; dy++) {
                        for (int dx = Math.max(0, x - 1); dx <= x + 1; dx++) {
                            if (dx == x && dy == y || dx >= col || dy >= row) // a位忽略，边界外忽略
                                continue;
                            // b位
                            int bRgb = zones[dy][dx];
                            if (bRgb == backgroundColorRgb) {
                                if (expand[dy][dx] == -1) {
                                    expand[dy][dx] = zones[y][x];
                                    expandPixel++;
                                } else if (expand[dy][dx] != zones[y][x]) {
                                    boundaries[dy][dx] = true;
                                }
                            } else if (bRgb != zones[y][x] && dx >= x && dy >= y) {
                                boundaries[y][x] = true;
                            }
                        }
                    }
                }
            }
            // 应用扩张
            if (expandPixel > 0) {
                for (int y = 0; y < row; y++) {
                    for (int x = 0; x < col; x++) {
                        if (expand[y][x] != -1) {
                            zones[y][x] = expand[y][x];
                        }
                    }
                }
                log.info("第 {} 轮扩张完成（最多 {} 轮），本次扩张 {} 个像素", i + 1, maxI, expandPixel);
            } else {
                log.info("第 {} 轮扩张完成，扩张提前结束", i + 1);
                break;
            }
        }
        log.info("边界生成完成");
    }

}
